-- DailyTaskM
-- Create by chendh
-- 日常任务模块

module("DailyTaskM", package.seeall);

-- 日常任务信息
local dailyTask = {};
-- 日常任务奖励信息
local dailyTaskBonus = {};

local whenStartDungeon = nil;
local whenPickUpItem   = nil;
local whenMonsterDie   = nil;
local whenMopUpSucc    = nil;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    dailyTask = {};
    dailyTaskBonus = {};

    local daily_task_CSV = get_daily_task_CSV();
    for i = 1, #daily_task_CSV do
        local taskId = daily_task_CSV[i].id
        local type   = daily_task_CSV[i].type;

        dailyTask[taskId] = daily_task_CSV[i];
        dailyTask[taskId].finish_bonus = parseBonus(daily_task_CSV[i].finish_bonus);
    end

    local daily_task_bonus_CSV = get_daily_task_bonus_CSV();
    for i = 1, #daily_task_bonus_CSV do
        local bonusId = daily_task_bonus_CSV[i].id
        local type   = daily_task_bonus_CSV[i].type;

        dailyTaskBonus[bonusId] = daily_task_bonus_CSV[i];
        dailyTaskBonus[bonusId].bonus = parseBonus(daily_task_bonus_CSV[i].bonus);
    end
end

-- 模块初始化
function init()
    loadCsv();

    -- 关注玩家离开地牢的事件
    EventMgr.removeAll("DailyTaskM");
    EventMgr.register("DailyTaskM", event.LEAVE_DUNGEON, function()
        whenLeaveDungeon();
    end);

    EventMgr.register("DailyTaskM", event.DUNGEON_AREADY, whenStartDungeon);

    EventMgr.register("DailyTaskM", event.PICK_UP_ITEM, whenPickUpItem);

    -- 关注怪物死亡的事件
    EventMgr.register("DailyTaskM", event.MONSTER_DIE_LOGIC, whenMonsterDie);

    -- 关注扫荡成功事件
    EventMgr.register("DailyTaskM", event.MOP_UP_DONE, whenMopUpSucc);
end

-- 解析奖品
function parseBonus(rule)
    local product = {}
    local index = 1
    local desc = string.explode(rule, "+")
    for _, info in pairs(desc) do
        local arr = string.explode(string.sub(info, 3, -2), ",");
        if string.startWith(info, "1(") then
            -- 这是物品奖励
            product = { 1, tonumber(arr[1]), tonumber(arr[2]), }
            index = index + 1
        elseif string.startWith(info, "2(") then
            -- 这是属性
            product = { 2, arr[1], tonumber(arr[2]), }
            index = index + 1
        end
    end

    return product
end

-- 检索任务信息
function query(taskId, path)
    local m = dailyTask[taskId];
    if not m or path == "" or not path then
        return m;
    else
        return m[path];
    end
end

-- 检索奖励信息
function queryBonus(bonusId, path)
    local m = dailyTaskBonus[bonusId];
    if not m or path == "" or not path then
        return m;
    else
        return m[path];
    end
end

-- 获取每日任务数
function getDailyTaskNum()
    return 2;
end

-- 获取累计任务数
function getCumulationTaskNum()
    return 10;
end

-- 获取玩家任务列表
function getTaskList()
    local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};

    return dailyTaskInfo.tasks or {};
end

-- 获取玩家今日刷新次数
function getRefreshTimes()
    local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};

    return dailyTaskInfo.refresh_num or 0;
end

-- 能否刷新
function canRefresh()
    -- 达到了刷新次数上限
    if isReachLimit() then
        return false;
    end

    local taskList = getTaskList();
    local allFinish = true;
    -- 必须有未完成的任务
    for _, taskInfo in ipairs(taskList) do
        if taskInfo.is_finish == 0 then
            allFinish = false;
        end
    end

    if allFinish then
        return false;
    end

    -- 必须支付得起消耗
    local cost = FormulaM.invoke("CALC_DAILY_TASK_REFRESH_COST");
    local ok = true

    if cost[1] == 1 then
        -- 物品消耗
        ok = ItemM.getAmount(ME.user, cost[2]) >= cost[3]
        if not ok then
            return "item_lack";           -- 道具不足
        end
    elseif cost[1] == 2 then
        -- 属性消耗
        ok = ME.user.dbase:query(cost[2], 0) >= cost[3]
        if not ok then
            return cost[2] ..  "_lack";   -- 属性不足
        end
    end

    return ok;
end

-- 是否达到了刷新次数上限
function isReachLimit()
    local limitCount = ServerStatusM.query("dalily_task_refresh_limit") or 0;
    if limitCount <= 0 then
        return false;
    end

    local dailyTaskInfo = ME.user.dbase:query("daily_task_info");
    if type(dailyTaskInfo) ~= "table" then
        return false;
    end

    local refreshNum = dailyTaskInfo["refresh_num"] or 0;
    if refreshNum >= limitCount then
        return true;
    end

    return false;
end

-- 刷新日常任务
function refreshTasks()
    local ret = canRefresh();
    if ret then
        Operation.cmd_refresh_daily_task();
    elseif type(ret) == "string" then
        alert(getLocStr(ret));
    end
end

-- 领取日常任务奖励
function getTaskBonus(index)
    local taskList = getTaskList();
    local taskInfo = taskList[index];

    if taskInfo and taskInfo.is_finish ~= 0 then
        -- 客户端先标记已领取任务奖励
        taskInfo["received"] = 1;

        -- 抛出属性变化事件，刷新下界面
        EventMgr.fire(event.USER_INFO_UPDATED, "daily_task_info");

        -- 客户端先自己发奖励
        local bonus = table.deepcopy(queryBonus(taskInfo.bonus_id, "bonus"));
        BonusM.doBonus(bonus, "dailyTaskBonus", false);

        local title = getLocStr("daily_task_bonus_title");
        showBonusNotifyForm(title, { bonus });

        -- 去服务端请求
        Operation.cmd_get_daily_task_bonus("task_bonus", index);
    end
end

-- 领取当日额外奖励
function getExtraBonus()
    if canGetExtraBonus() then
        -- 客户端先标记已领取额外奖励
        local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};
        dailyTaskInfo.extra_bonus = 1;
        ME.user.dbase:set("daily_task_info", dailyTaskInfo);

        -- 抛出属性变化事件，刷新下界面
        EventMgr.fire(event.USER_INFO_UPDATED, "daily_task_info");

        -- 客户端先自己发奖励
        local bonusList = FormulaM.invoke("CALC_DAILY_TASK_EXTRA_BONUS");
        for _, bonus in pairs(bonusList) do
            BonusM.doBonus(bonus, "dailyTaskExtraBonus", false);
        end

        -- 去服务端请求
        Operation.cmd_get_daily_task_bonus("extra_bonus", 0);

        return bonusList;
    end

    return false;
end

-- 能否领取额外奖励
function canGetExtraBonus()
    local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};

    if type(dailyTaskInfo.extra_bonus) ~= "number" or dailyTaskInfo.extra_bonus ~= 0 then
        return false;
    end

    for _,  taskInfo in ipairs(getTaskList()) do
        if taskInfo.is_finish ~= 1 then
            return false;
        end
    end

    return true;
end

-- 领取累积奖励
function getCumulationBonus()
    if canGetCumulationBonus() then
        -- 客户端先标记已领取累计奖励
        local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};
        dailyTaskInfo.cumulation_task_num = 0;
        ME.user.dbase:set("daily_task_info", dailyTaskInfo);

        -- 抛出属性变化事件，刷新下界面
        EventMgr.fire(event.USER_INFO_UPDATED, "daily_task_info");

        -- 客户端先自己发奖励
        local bonusList = FormulaM.invoke("CALC_DAILY_TASK_CUMULATION_BONUS");
        for _, bonus in pairs(bonusList) do
            BonusM.doBonus(bonus, "dailyTaskCumulationBonus", false);
        end

        -- 去服务端请求
        Operation.cmd_get_daily_task_bonus("cumulation_bonus", 0);

        return bonusList;
    end

    return false;
end

-- 能否领取累积奖励
function canGetCumulationBonus()
    local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};

    if type(dailyTaskInfo.cumulation_task_num) == "number" and dailyTaskInfo.cumulation_task_num >= getCumulationTaskNum() then
        return true;
    end

    return false;
end

-- 进入下一层
whenStartDungeon = function()
    -- 获取当前所在的迷宫编号和层数
    local dungeonId = DungeonM.getDungeonId();
    local layer = DungeonM.currentLayer();

    for _, taskInfo in ipairs(getTaskList()) do
        local taskId = taskInfo.id;
        local taskType = query(taskId, "type");
        local activePet = ME.user.dbase:query("active_pet", 0);
        local campId = CampM.getPetCamp(activePet);

        if taskInfo.dungeon_id == dungeonId and taskInfo.floor == layer and
            taskInfo.is_finish == 0 and taskType == "inherit" and campId == taskInfo.camp_id then
            tryToFinishTask(taskId);

            -- TODO: 专门加的事件
            EventMgr.fire(event.FINISH_INHERIT_TASK, {["taskId"] = taskId});
        end
    end
end

-- 拾取到道具
whenPickUpItem = function(args)
    local dungeonId = DungeonM.getDungeonId();
    local layer = DungeonM.currentLayer();

    for _, taskInfo in ipairs(getTaskList()) do
        if taskInfo.dungeon_id == dungeonId and taskInfo.floor == layer then
            local taskId = taskInfo.id;
            if query(taskId, "task_item") == args.bonus[2] then
                -- 尝试完成任务
                tryToFinishTask(taskId);
            end
        end
    end
end

-- 怪物死亡事件
whenMonsterDie = function(args)
    local monster = args.monster;
    if not monster then
        return;
    end

    local classId = monster.classId;
    local dungeonId = DungeonM.getDungeonId();
    local layer = DungeonM.currentLayer();

    for _, taskInfo in ipairs(getTaskList()) do
        if taskInfo.dungeon_id == dungeonId and taskInfo.floor == layer then
            local taskId = taskInfo.id;
            if query(taskId, "task_monster") == classId then
                -- 尝试完成任务
                tryToFinishTask(taskId);
            end
        end
    end
end

-- 尝试完成任务
function tryToFinishTask(taskId, isReward)
    for _, taskInfo in ipairs(getTaskList()) do
        if taskId == taskInfo.id then
            if taskInfo.is_finish == 0 and taskInfo.received == 0 then
                -- 设置为已完成
                taskInfo.is_finish = 1;

                -- 累计完成任务数量增加
                local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};
                dailyTaskInfo.cumulation_task_num = (dailyTaskInfo.cumulation_task_num or 0) + 1;
                if dailyTaskInfo.cumulation_task_num > getCumulationTaskNum() then
                    dailyTaskInfo.cumulation_task_num = getCumulationTaskNum();
                end

                -- 给予任务完成时的奖励
                local taskFinishBonus = table.deepcopy(query(taskId, "finish_bonus"));
                if isReward ~= false then
                    if type(taskFinishBonus) == "table" and #taskFinishBonus > 0 then
                        BonusM.doBonus(taskFinishBonus, "dailyTaskFinishBonus");
                    end
                end

                -- 更新完成记录
                local taskHistory = ME.user.dbase:query("daily_task_history", {});
                if table.indexOf(taskHistory, taskId) == - 1 then
                    table.insert(taskHistory, taskId);
                    ME.user.dbase:set("daily_task_history", taskHistory);
                end

                -- 抛出日常任务完成事件
                EventMgr.fire(event.DAILY_TASK_FINISH, taskFinishBonus);
            end
        end
    end
end

-- 检查当前地牢本层是否有日常任务未完成
function getUnfinishDailyTask()
    local dungeonId = DungeonM.getDungeonId();
    local layer = DungeonM.currentLayer();

    for _, taskInfo in ipairs(getTaskList()) do
        if taskInfo.dungeon_id == dungeonId and taskInfo.floor == layer then
            -- 如果本层有日常任务未完成，则返回任务信息
            if taskInfo.is_finish ~= 1 then
                return taskInfo;
            end
        end
    end

    return {};
end

-- 标记任务已经提示过了
function reminded(taskId)
    for _, taskInfo in ipairs(getTaskList()) do
        if taskInfo.id == taskId then
            taskInfo.reminded = true;
        end
    end
end

-- 离开地牢的处理
function whenLeaveDungeon()
    -- 玩家离开地牢时干掉所有的reminded
    for _, taskInfo in ipairs(getTaskList()) do
        taskInfo.reminded = nil;
    end
end

-- 关注玩家扫荡事件
whenMopUpSucc = function(args)
    local dungeonId = args.dungeonId;

    local isFinished = AchievementM.hasFinishTaskProp(dungeonId);
    if not isFinished then
        return;
    end

    for _, taskInfo in ipairs(getTaskList()) do
        local taskId = taskInfo.id;

        if taskInfo.dungeon_id == dungeonId then
            tryToFinishTask(taskId, false);
        end
    end
end

-- 检查version是不是过期了
function checkTaskVersion()
    local dailyTaskInfo = ME.user.dbase:query("daily_task_info") or {};
    local version = dailyTaskInfo["version"];
    if type(version) ~= "number" then
        return;
    end

    -- 今天零点的时间戳
    local zeroTime = TimeM.calcZeroTime();
    if version < zeroTime then
        -- 版本比今天零时小,主动请求刷新一次
        Operation.cmd_update_daily_task_info();
    end
end